﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.Reflection;
using System.IO;
using TestMonkey.Models;
using RazorEngine;
using TestMonkey.Templates;
using RazorEngine.Templating;

namespace TestMonkey
{
	public class TestBuilder
	{
		readonly ObservableCollection<TemplateProcessor> m_Templates = new ObservableCollection<TemplateProcessor>();

		public ObservableCollection<TemplateProcessor> Templates
		{
			get { return m_Templates; }
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")]
		public void Run(Setup setup)
        {
			var tplCfg = new RazorEngine.Configuration.TemplateServiceConfiguration();
			tplCfg.BaseTemplateType = typeof(CodeTemplateBase);
			var tplSvc = new TemplateService(tplCfg);
			Razor.SetTemplateService(tplSvc);

            //Razor.SetTemplateBase(typeof(CodeTemplateBase));

            var targetAssembly = Assembly.ReflectionOnlyLoadFrom(setup.SourceDll);

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => CurrentDomain_AssemblyResolve(setup, e);

            foreach (var type in targetAssembly.GetTypes())
            {
                if (type.Name.StartsWith("<>"))
                    continue;
                if (type.Name.Contains("`"))
                    continue;
                if (!type.IsPublic)
                    continue;
                if (type.IsSubclassOf(typeof(Delegate)))
                    continue;

                Console.WriteLine("Processing " + type.FullName);
                ProcessType(setup, type);
            }
        }


        static Assembly CurrentDomain_AssemblyResolve(Setup setup, ResolveEventArgs args)
		{
			var result = CurrentDomain_AssemblyResolve(args, new DirectoryInfo(Path.GetDirectoryName(args.RequestingAssembly.Location)));
			if (result != null)
				return result;

            foreach (var path in setup.SearchPaths)
			{
				result = CurrentDomain_AssemblyResolve(args, new DirectoryInfo(path));
				if (result != null)
					return result;
			}

            throw new SetupException("Could not find " + args.Name + " for " + args.RequestingAssembly);

			//return null;
		}


        static Assembly CurrentDomain_AssemblyResolve(ResolveEventArgs args, DirectoryInfo searchPath)
        {
            if (args == null)
                throw new ArgumentNullException("args", "args is null.");
            if (searchPath == null)
                throw new ArgumentNullException("searchPath", "searchPath is null.");

            foreach (var file in searchPath.GetFiles("*.dll"))
            {
                string name = Path.GetFileNameWithoutExtension(file.Name);
                if (file.Name.ToLowerInvariant().EndsWith(".dll") && args.Name.StartsWith(name + ',', StringComparison.InvariantCultureIgnoreCase))
                    return Assembly.ReflectionOnlyLoadFrom(file.FullName);
            }

            return null;
        }

        void ProcessType(Setup setup, Type type)
        {
            var model = new ModelRoot(setup, type);
            foreach (var template in Templates)
            {
                //try
                //{
                    template.ProcessTemplate(model);
                //}
                //catch(Exception ex)
                //{
                //    Console.WriteLine();
                //    Console.WriteLine("Error in template:" + template.GetType().Name + Environment.NewLine + ex);
                //    Console.WriteLine();

                //}
            }
        }


	}
}
